
import chalk from "chalk";
import { assert } from "node-opcua-assert";
import should from "should";
import sinon from "sinon";

import {
    OPCUAClient,
    AttributeIds,
    resolveNodeId,
    StatusCodes,
    DataType,
    TimestampsToReturn,
    NodeId,
    callConditionRefresh,
    coerceNodeId,
    Variant,
    LocalizedText,
    sameNodeId,
    ClientSubscription,
    UAVariable,
    UAAlarmConditionEx,
    NodeIdLike
} from "node-opcua";

import { constructEventFilter, ofType, s } from "node-opcua-service-filter";

import { make_debugLog, checkDebugFlag } from "node-opcua-debug";

import { construct_demo_alarm_in_address_space } from "node-opcua-address-space/testHelpers";
import { perform_operation_on_subscription } from "../../../test_helpers/perform_operation_on_client_session";
import { f } from "../../discovery/helpers/_helper";
import { wait, waitUntilCondition } from "../../../test_helpers/utils";
import { describeWithLeakDetector as describe } from "node-opcua-leak-detector";

const debugLog = make_debugLog("TEST");
const doDebug = checkDebugFlag("TEST");


async function wait_a_little_bit_to_let_events_to_be_processed() {
    await wait(400);
}

const stepInfo = (str: string) => {
    if (doDebug || true) {
        console.log(chalk.yellow("   --> ") + chalk.cyan(str));
    }
};
export function t(test: any) {
    describe("A&C monitoring conditions", function () {
        let client: OPCUAClient;

        beforeEach(() => {
            // add a condition to the server
            // Server - HasNotifier -> Tank -> HasEventSource -> TankLevel -> HasCondition -> TankLevelCondition

            const addressSpace = test.server.engine.addressSpace;

            construct_demo_alarm_in_address_space(test, addressSpace);

            client = OPCUAClient.create({
                clientName: __filename
            });
        });
        afterEach(() => {
        });

        function dump_field_values(fields: string[], values: Variant[]) {

            fields.map((field, index) => [field, values[index]] as [string, Variant]).forEach(([field, value]) => {
                let str = "";
                if (value.dataType === DataType.NodeId) {
                    const node = test.server.engine.addressSpace.findNode(value?.value || "null");
                    str = node ? node.browseName.toString() : " Unknown Node";
                }
                console.log(
                    chalk.yellow(field.padEnd(50)),
                    (value || "").toString() + " " + chalk.white.bold(str)
                );
            });
            console.log("--------------------");
        }

        function extract_node_id_value_for_condition_type_field(result: Variant[]): Variant {
            // this is the last one in result
            return result[result.length - 1];
        }

        const fields = [
            "EventId",
            "ConditionName",
            "ConditionClassName",
            "ConditionClassId",
            "SourceName",
            "SourceNode",
            "BranchId",
            "EventType",
            "SourceName",
            "ReceiveTime",
            "Severity",
            "Message",
            "Retain",
            "Comment",
            "Comment.SourceTimestamp",
            "EnabledState",
            "EnabledState.Id",
            "EnabledState.EffectiveDisplayName",
            "EnabledState.TransitionTime",
            "LastSeverity",
            "LastSeverity.SourceTimestamp",
            "Quality",
            "Quality.SourceTimestamp",
            "Time",
            "ClientUserId",
            "AckedState",
            "AckedState.Id",
            "ConfirmedState",
            "ConfirmedState.Id",
            // "LimitState", is an object
            "LimitState.CurrentState",
            "LimitState.CurrentState.Id",
            "ActiveState",
            "ActiveState.Id",
            "ConditionId"
        ];

        function extract_value_for_field(fieldName: string, result: Variant[]): Variant {
            should.exist(result);
            const index = fields.indexOf(fieldName);
            should(index).be.greaterThanOrEqual(0,
                " cannot find fieldName in list  : fiedName =" + fieldName + " list: " + fields.join(" "));
            return result[index];
        }

        const eventFilter = constructEventFilter(fields, ofType("BaseEventType"));

        async function given_an_installed_event_monitored_item(test: any, subscription: ClientSubscription) {

            stepInfo("given an installed event monitored item ...");
            // A spy to detect event when they are raised by the sever
            test.spy_monitored_item1_changes = sinon.spy();
            // let create a monitored item to monitor events emitted by the Tank and
            // transmitted by the Server Object.

            const readValue = {
                attributeId: AttributeIds.EventNotifier, // << EventNotifier
                nodeId: resolveNodeId("Server")
            };
            const requestedParameters = {
                discardOldest: true,
                filter: eventFilter,
                queueSize: 10,
                samplingInterval: 10
            };

            test.monitoredItem1 = await subscription.monitor(readValue, requestedParameters, TimestampsToReturn.Both);

            await wait(100);

            // let's install the spy on the 'changed' event
            test.monitoredItem1.on("changed", test.spy_monitored_item1_changes);

        }

        it("A&C-01 -  Limit Alarm should trigger Event when ever the input node goes out of limit", async () => {
            await perform_operation_on_subscription(
                client,
                test.endpointUrl,
                async (session, subscription) => {

                    await given_an_installed_event_monitored_item(test, subscription);


                    assert(test.tankLevelCondition.raiseNewCondition);
                    test.tankLevelCondition.raiseNewCondition = sinon.spy(test.tankLevelCondition, "raiseNewCondition");
                    test.tankLevelCondition.raiseNewCondition.calledOnce.should.eql(false);

                    stepInfo("when tank level is overfilled ...");
                    // let's simulate the tankLevel going to 99%
                    // the alarm should be raised
                    test.tankLevel.setValueFromSource({
                        dataType: "Double",
                        value: 0.99
                    });

                    stepInfo("... the alarm should be raised ...");
                    test.tankLevelCondition.raiseNewCondition.calledOnce.should.eql(true);
                    test.tankLevelCondition.limitState.getCurrentState().should.eql("HighHigh");


                    stepInfo("      then we should check that alarm is raised ...");
                    await new Promise<void>((resolve) => {
                        test.monitoredItem1.once("changed", () => resolve());
                    });
                    test.spy_monitored_item1_changes.callCount.should.eql(1);

                    stepInfo(" ... when the value goes off limit");
                    test.tankLevel.setValueFromSource({
                        dataType: "Double",
                        value: 0.991
                    });
                    // clear_condition_retain_flag

                    // ----------------------------
                    // Clear existing conditions
                    // -------------------------------
                    // clear_condition_retain_flag
                    test.tankLevelCondition.currentBranch().setRetain(false);
                });
        });

        it("A&C-02 - ConditionRefresh", async () => {


            async function wait_until_refresh_end() {

                stepInfo("wait until a RefreshEndEventType is received");
                await new Promise<void>((resolve, reject) => {


                    if (test.spy_monitored_item1_changes.callCount === 3) {
                        stepInfo(chalk.red("spy_monitored_item1_changes.callCount = " + test.spy_monitored_item1_changes.callCount));
                        resolve();
                    }
                    const timer = setTimeout(() => {
                        stepInfo(chalk.red("wait_until_refresh_end has  timed out"));
                        reject(new Error("wait_until_refresh_end timed out"));
                    }, 10000);

                    let alreadyCalled = false;
                    const lambda = (values: Variant[]) => {
                        if (alreadyCalled) {
                            return;
                        }
                        const eventType = extract_value_for_field("EventType", values);
                        console.log("eventType = ", eventType.toString());
                        if (sameNodeId(eventType.value, resolveNodeId("RefreshEndEventType"))) {
                            if (alreadyCalled) {
                                return;
                            }
                            alreadyCalled = true;
                            test.monitoredItem1.removeListener("changed", lambda);
                            if (timer) {
                                clearTimeout(timer);
                            }
                            setImmediate(() => {
                                stepInfo(chalk.green("   ... received RefreshEndEventType !"));
                                resolve();
                            });
                        }
                    };
                    // lets add a a event handler to detect when the Event has been
                    // raised we we will call ConditionRefresh
                    test.monitoredItem1.on("changed", lambda);
                });
            }



            await perform_operation_on_subscription(
                client,
                test.endpointUrl,
                async (session, subscription) => {

                    stepInfo("initialization... tank level set to 90%");
                    test.tankLevel.setValueFromSource({ dataType: "Double", value: 0.9 });

                    await given_an_installed_event_monitored_item(test, subscription);

                    stepInfo("When the client is calling ConditionRefresh");
                    {

                        // now client send a condition refresh
                        // let's call condition refresh
                        test.spy_monitored_item1_changes.resetHistory();
                        await callConditionRefresh(session, subscription.subscriptionId);
                        await wait_until_refresh_end();

                    }



                    stepInfo("then we should check that event is raised after client calling ConditionRefresh");
                    {
                        let values = test.spy_monitored_item1_changes.getCall(0).args[0];
                        let eventType = extract_value_for_field("EventType", values);
                        eventType.value.toString().should.eql("ns=0;i=2787"); // RefreshStartEventType
                        dump_field_values(fields, values);

                        values = test.spy_monitored_item1_changes.getCall(1).args[0];
                        eventType = extract_value_for_field("EventType", values);
                        eventType.value.toString().should.eql("ns=0;i=9341"); //ExclusiveLimitAlarmType
                        dump_field_values(fields, values);

                        values = test.spy_monitored_item1_changes.getCall(2).args[0];
                        eventType = extract_value_for_field("EventType", values);
                        eventType.value.toString().should.eql("ns=0;i=2788"); // RefreshEndEventType
                        dump_field_values(fields, values);

                        test.spy_monitored_item1_changes.callCount.should.eql(3);


                    }
                    stepInfo(" condition refresh has been send to server , now waiting for events");

                    stepInfo("then when server raises a new condition_event");
                    {


                        test.spy_monitored_item1_changes.resetHistory();
                        // ---------------------------
                        // create a retain condition
                        const tankLevelCondition = test.tankLevelCondition;
                        tankLevelCondition.currentBranch().setRetain(true);
                        tankLevelCondition.raiseNewCondition({
                            message: "Tank almost 80% full",
                            severity: 200,
                            quality: StatusCodes.Good
                        });
                        await waitUntilCondition(async () => {
                            return test.spy_monitored_item1_changes.callCount === 1;
                        }, 1000, " wait for condition raiseNewCondition to be detected");
                    }

                    stepInfo("Verification");
                    {
                        await wait(100);
                        test.spy_monitored_item1_changes.callCount.should.eql(1);
                        const values = test.spy_monitored_item1_changes.getCall(0).args[0];
                        values[7].value.toString().should.eql("ns=0;i=9341"); //ExclusiveLimitAlarmType
                        //xx dump_field_values(fields,values);
                        test.spy_monitored_item1_changes.resetHistory();
                    }
                    stepInfo("when client calling ConditionRefresh again");
                    {
                        test.spy_monitored_item1_changes.resetHistory();
                        await callConditionRefresh(session, subscription.subscriptionId);
                        await wait_until_refresh_end();
                    }

                    stepInfo("Verification");
                    {
                        await wait(100);
                    }

                    stepInfo("then_we_should_check_that_event_is_raised_after_client_calling_ConditionRefresh_again");
                    {
                        //   test.spy_monitored_item1_changes.callCount.should.eql(3);

                        let values = test.spy_monitored_item1_changes.getCall(0).args[0];
                        values[7].value.toString().should.eql("ns=0;i=2787"); // RefreshStartEventType
                        //xx dump_field_values(fields,values);

                        values = test.spy_monitored_item1_changes.getCall(1).args[0];
                        values[7].value.toString().should.eql("ns=0;i=9341"); // ExclusiveLimitAlarmType
                        //xx dump_field_values(fields,values);

                        values = test.spy_monitored_item1_changes.getCall(2).args[0];
                        values[7].value.toString().should.eql("ns=0;i=2788"); // RefreshEndEventType
                        //dump_field_values(fields,values);

                        test.spy_monitored_item1_changes.resetHistory();
                    }

                    stepInfo("Verification")
                    {
                        const tankLevelCondition = test.tankLevelCondition;
                        tankLevelCondition.currentBranch().setRetain(false);
                        tankLevelCondition.raiseNewCondition();
                    }
                });
        });

        describe("test on Disabled conditions", function () {
            /*
       For any Condition that exists in the AddressSpace the Attributes and the following
       Variables will continue to have valid values even in the Disabled state; EventId, Event
       Type, Source Node, Source Name, Time, and EnabledState. Other properties may no
       longer provide current valid values. All Variables that are no longer provided shall
       return a status of Bad_ConditionDisabled. The Event that reports the Disabled state
       should report the properties as NULL or with a status of Bad_ConditionDisabled.
       */
            it("A&C-03 should raise an event when a Condition get disabled", async () => {
                await perform_operation_on_subscription(
                    client,
                    test.endpointUrl,
                    async (session, subscription) => {
                        await f(async function given_a_enabled_condition() {
                            test.tankLevelCondition.enabledState.setValue(true);
                            test.tankLevelCondition.enabledState.getValue().should.eql(true);
                        })();

                        await given_an_installed_event_monitored_item(test, subscription);


                        await f(async function when_the_condition_is_disabled_by_the_client() {
                            //xx test.tankLevelCondition.enabledState.setValue(false);
                            //xx test.tankLevelCondition.enabledState.getValue().should.eql(false);
                            const methodToCalls = [];
                            methodToCalls.push({
                                objectId: test.tankLevelCondition.nodeId,
                                methodId: coerceNodeId("ns=0;i=9028"), // ConditionType#Disable Method nodeID
                                inputArguments: []
                            });

                            await session.call(methodToCalls)
                        })();

                        await f(async function then_we_should_verify_that_the_client_has_received_a_notification() {
                            await wait(500);
                            test.spy_monitored_item1_changes.callCount.should.eql(1);
                        })();

                        await f(async function and_we_should_verify_that_the_propertie_are_null_or_with_status_Bad_ConditionDisabled() {
                            // The Event that reports the Disabled state
                            // should report the properties as NULL or with a status of Bad_ConditionDisabled.
                            const results = test.spy_monitored_item1_changes.getCall(0).args[0];
                            //xx dump_field_values(fields, results);

                            const conditionDisabledVar = new Variant({
                                dataType: DataType.StatusCode,
                                value: StatusCodes.BadConditionDisabled
                            });

                            // shall be valid EventId, EventType, SourceNode, SourceName, Time, and EnabledState
                            // other shall be invalid

                            const value_severity = extract_value_for_field("Severity", results);
                            debugLog("value_severity ", extract_value_for_field("Severity", results).toString());
                            value_severity.should.eql(conditionDisabledVar);

                        })();
                    });
            });

            xit("A&C-04 EventId, EventType, Source Node, Source Name, Time, and EnabledState shall return valid values when condition is disabled ", async () => {
                //             "EventId",
                //             "ConditionName",
                //             "ConditionClassName",
                //             "ConditionClassId",
                //             "SourceName",
                //             "SourceNode",
                //             "BranchId",
                //             "EventType",
                //             "SourceName",
                //             "ReceiveTime",
                //             "Severity",
                //             "Message",
                //             "Retain",
                //             "Comment",
                //             "Comment.SourceTimestamp",
                //             "EnabledState",
                //             "EnabledState.Id",
                //             "EnabledState.EffectiveDisplayName",
                //             "EnabledState.TransitionTime",
                //             "LastSeverity",
                //             "LastSeverity.SourceTimestamp",
                //             "Quality",
                //             "Quality.SourceTimestamp",
                //             "Time",
                //                     "ClientUserId"

            });

            xit("A&C-05 reading no longer provided variables of a disabled Condition shall return Bad_ConditionDisabled", async () => {
            });
        });

        it("A&C-06 should raise an (OPCUA) event when commenting a Condition ", async () => {
            const levelNode = test.tankLevel;
            const alarmNode = test.tankLevelCondition;

            await perform_operation_on_subscription(
                client,
                test.endpointUrl,
                async (session, subscription) => {

                    await (async function given_a_enabled_condition() {
                        alarmNode.enabledState.setValue(true);
                        alarmNode.enabledState.getValue().should.eql(true);
                    })();

                    await given_an_installed_event_monitored_item(test, subscription);

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    await (async function when_a_notification_event_is_raised_by_the_condition() {
                        test.spy_monitored_item1_changes.resetHistory();
                        levelNode.setValueFromSource({ dataType: "Double", value: 1000 });
                    })();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    await (async function then_we_should_verify_than_the_event_received_is_correct() {
                        // let dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                        // dump_field_values(fields,dataValues);
                        // dataValues = test.spy_monitored_item1_changes.getCall(1).args[0];
                        // dump_field_values(fields,dataValues);

                        test.spy_monitored_item1_changes.callCount.should.eql(2, "one event should have been raised");
                    })();

                    await (async function when_we_set_a_comment() {
                        test.spy_monitored_item1_changes.resetHistory();
                        // The EventId is identifying a particular Event Notification where a state was reported for a Condition.
                        const eventId = alarmNode.eventId.readValue().value.value;

                        const alarmNodeId = alarmNode.nodeId;
                        await session.addCommentCondition(alarmNodeId, eventId, "SomeComment!!!");
                    })();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    await (async function we_should_verify_that_an_event_has_been_raised() {
                        let dataValues;
                        // we are expecting 2 events here :
                        // * a new event for the main branch because spec says:
                        //     Comment, severity and quality are important elements of Conditions and any change to them
                        //     will cause Event Notifications.
                        // * a AuditConditionCommentEventType
                        test.spy_monitored_item1_changes.callCount.should.eql(2, "Two events should have been raised");

                        // lets extract the eventId on which the comment was added
                        // we can find in on firt event notificiation
                        dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                        //xx dump_field_values(fields,dataValues);
                        const eventId = extract_value_for_field("EventId", dataValues).value;
                        eventId.should.be.instanceOf(Buffer);

                        // let verify the AuditConditionCommentEventType data
                        dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                        //xx dump_field_values(fields,dataValues);
                        const eventType = extract_value_for_field("EventType", dataValues).value.toString();
                        eventType.should.eql("ns=0;i=2829"); // AuditConditionCommentEventType
                        const eventId_Step0 = extract_value_for_field("EventId", dataValues).value;
                        should(eventId_Step0).be.instanceOf(Buffer);
                        eventId_Step0.toString("hex").should.eql(eventId.toString("hex"));

                        // let verify the event raised by the condition, due to the comment update
                        dataValues = test.spy_monitored_item1_changes.getCall(1).args[0];
                        //  dump_field_values(fields,dataValues);
                        //xx The EventId field shall contain the id of the event for which the comment was added.
                        extract_value_for_field("BranchId", dataValues).value.should.eql(NodeId.nullNodeId);
                        extract_value_for_field("ConditionName", dataValues).value.should.eql("TankLevelCondition");
                        extract_value_for_field("SourceName", dataValues).value.should.eql(levelNode.browseName.toString());
                        extract_value_for_field("Comment", dataValues).value.text.toString().should.eql("SomeComment!!!");
                        const eventId_New = extract_value_for_field("EventId", dataValues).value;
                        eventId_New.toString("hex").should.not.eql(eventId.toString("hex"));

                        extract_node_id_value_for_condition_type_field(dataValues)
                            .value.toString()
                            .should.eql(alarmNode.nodeId.toString());

                        alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");
                    })();
                });
        });

        it("A&C-07 should raise an (INTERNAL) event when client is commenting", async () => {
            const levelNode = test.tankLevel;
            const alarmNode = test.tankLevelCondition;

            const addCommentSpy = sinon.spy();
            alarmNode.on("addComment", addCommentSpy);
            const the_new_comment = " The NEW COMMENT !!!";

            await perform_operation_on_subscription(
                client,
                test.endpointUrl,
                async (session, subscription) => {

                    // given_a_enabled_condition
                    alarmNode.enabledState.setValue(true);
                    alarmNode.enabledState.getValue().should.eql(true);

                    addCommentSpy.callCount.should.eql(0);

                    // given_that_the_condition_has_raised_an_event
                    levelNode.setValueFromSource({ dataType: "Double", value: 1000 });

                    // when_we_set_a_comment
                    const eventId = alarmNode.eventId.readValue().value.value;
                    should.exist(eventId, "alarm must have raised an event");

                    const alarmNodeId = alarmNode.nodeId;
                    await session.addCommentCondition(alarmNodeId, eventId, the_new_comment);

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    // then_we_should_verify_that_the_internal_addComment_event_has_been_raised

                    addCommentSpy.callCount.should.eql(1);
                    addCommentSpy.getCall(0).args[0].should.be.instanceOf(Buffer); // eventId
                    addCommentSpy.getCall(0).args[1].should.be.instanceOf(LocalizedText);
                    addCommentSpy.getCall(0).args[2].constructor.name.should.eql("ConditionSnapshotImpl");

                    addCommentSpy.getCall(0).args[1].text.should.eql(the_new_comment);

                    // in this case, we should not received a AuditConditionCommentEventType
                    // because comment was not added through the AddComment method !

                    alarmNode.removeListener("addComment", addCommentSpy);
                });
        });

        xit("A&C-08 should raise an event when acknowledging an AcknowledgeableCondition ", async () => {
        });

        xit("A&C-09 a condition should expose ReadOnly condition values", async () => {
        });

        async function perform_test_with_condition(
            eventTypeNodeId: NodeIdLike,
            levelNode: UAVariable,
            alarmNode: UAAlarmConditionEx
        ) {
            // this test implements the example of the Spec 1.03 part 9 page, cited below:
            //     B.1.3 Server maintains previous states
            //            This example is for Servers that are able to maintain previous states of a Condition and
            //            therefore create and maintain Branches of a single Condition.
            //            The figure below illustrates the use of branches by a Server requiring acknowledgement of all
            //            transitions into Active state, not just the most recent transition.
            //            In this example no acknowledgement is required on a transition into an inactive state.
            //            All Events are coming from the same Condition instance and have therefore the same ConditionId.
            //
            // ___________________________________________________________________________________________
            // branch-2                                           o-----------------------o    Confirmed=true
            //
            //                                                                           +o     Acked
            //                                                    o----------------------+
            //                                                    o-----------------------o    Active (true)
            // ___________________________________________________________________________________________
            // branch-1                          o------------+   .         +o                Confirmed
            //                                                +-------------+
            //                                                +--------------o                Acked
            //                                   o------------+   .
            //                                   o---------------------------o                Active (true)
            // ___________________________________________________._______________________________________
            //                                                .   .         .
            // -------------------+     +---------------------------------------------------- confirmed
            //                    +-----+                     .   .         .
            // ----------+        +---------+   +----------+  .   +-------------------------  Acked
            //           +--------+     .   +---+          +------+         .
            //           +-----------+  .   +---+          +------+         .
            // ----------+        .  +------+   +----------+  .   +--------------------------  Active
            //           .        .  .  .   .   .             .             .
            //          (1)      (2)(3)(4) (5) (6)        (8)(9) (10)      (12)       (13)
            //                                 (7)               (11)                 (14)
            //
            // EventId BranchId Active Acked  Confirmed Retain   Description
            // a/      null     False  True   True      False    Initial state of Condition.
            // 1       null     True   False  True      True     Alarm goes active.
            // 2       null     True   True   True      True     Condition acknowledged requires Confirm
            // 3       null     False  True   False     True     Alarm goes inactive.
            // 4       null     False  True   True      False    Confirmed
            // 5       null     True   False  True      True     Alarm goes active.
            // 6       null     False  True   True      True     Alarm goes inactive.
            // 7       1        True   False  True      True     b) Prior state needs acknowledgment. Branch #1 created.
            // 8       null     True   False  True      True     Alarm goes active again.
            // 9       1        True   True   False     True     Prior state acknowledged, Confirm required.
            // 10      null     False  True   True      True     b) Alarm goes inactive again.
            // 11      2        True   False  True      True     Prior state needs acknowledgment. Branch #2 created.
            // 12      1        True   True   True      False    Prior state confirmed. Branch #1 deleted.
            // 13      2        True   True   True      False    Prior state acknowledged, Auto Confirmed by system Branch #2 deleted.
            // 14      Null     False   True  True      False    No longer of interest.
            //
            // a/The first row is included to illustrate the initial state of the Condition.
            //    This state will not be reported by an Event.
            //
            //  If the current state of the Condition is acknowledged then the Acked flag is set and the new state is reported (Event
            //  #2). If the Condition state changes before it can be acknowledged (Event #6) then a branch state is reported (Event
            //  #7). Timestamps for the Events #6 and #7 is identical.
            //       The branch state can be updated several times (Events #9) before it is cleared (Event #12).
            //       A single Condition can have many branch states active (Events #11)
            // b/ It is recommended as in this table to leave Retain=True as long as there exist previous states (branches)

            let eventId_Step0: Buffer | null = null;
            let eventId_Step2: Buffer | null = null; // after acknowledge
            let branch1_NodeId: NodeId | null = null;
            let branch1_EventId: Buffer | null = null;

            let branch2_NodeId: NodeId | null = null;
            let branch2_EventId: Buffer | null = null;
            let dataValues;

            await perform_operation_on_subscription(
                client,
                test.endpointUrl,
                async (session, subscription) => {


                    async function initial_state_of_condition() {
                        levelNode.setValueFromSource({ dataType: "Double", value: 0.5 });

                        alarmNode.currentBranch().setConfirmedState(true);
                        alarmNode.currentBranch().setAckedState(true);

                        for (const b of alarmNode.getBranches()) {
                            b.setConfirmedState(true);
                            b.setAckedState(true);
                            alarmNode.deleteBranch(b);
                        }

                        alarmNode.activeState!.getValue().should.eql(false);
                        alarmNode.confirmedState!.getValue().should.eql(true, "confirmedState supposed to be set");
                        alarmNode.ackedState.getValue().should.eql(true, "ackedState supposed to be set");

                        alarmNode.getBranchCount().should.eql(0);


                    }

                    async function alarm_goes_active() {
                        // sanity check - verify that previous state was inactive
                        levelNode.readValue().value.value.should.eql(0.5);
                        alarmNode.activeState.getValue().should.eql(false);

                        stepInfo("alarm goes active : tank level set to 99%");
                        // set the value so it exceed one of the limit => alarm will be raised
                        levelNode.setValueFromSource({ dataType: "Double", value: 0.99 });
                        alarmNode.activeState.getValue().should.eql(true);

                    }

                    async function alarm_goes_inactive() {
                        // sanity check - verify that previous state was active
                        levelNode.readValue().value.value.should.eql(0.99);
                        alarmNode.activeState.getValue().should.eql(true);

                        stepInfo("alarm goes inactive : tank level set to 55%");

                        // set the value so it is in normal range  => alarm no active
                        levelNode.setValueFromSource({ dataType: "Double", value: 0.5 });
                        alarmNode.activeState.getValue().should.eql(false, "expecting alarm to be inactive");

                    }

                    async function condition_acknowledged_requires_confirm() {

                        stepInfo("condition is acknowledged (requires confirm)");
                        should(alarmNode.nodeId).be.instanceOf(NodeId);

                        const conditionId = alarmNode.nodeId;
                        const eventId = eventId_Step0!;
                        await session.acknowledgeCondition(conditionId, eventId, "Some comment");
                    }

                    async function condition_is_confirmed() {

                        const conditionId = alarmNode.nodeId;
                        const eventId = eventId_Step0!;
                        await session.confirmCondition(conditionId, eventId, "Some comment");
                        stepInfo("condition is confirmed");
                    }

                    async function verify_that_branch_one_is_created() {

                    }

                    async function branch_two_acknowledged_and_auto_confirmed_by_system_verify_branch_two_is_deleted() {
                        const branch = alarmNode.findBranchForEventId(branch2_EventId);

                        alarmNode.acknowledgeAndAutoConfirmBranch(branch!, "AutoConfirm");
                    }

                    // a/ initial_state_of_condition
                    await initial_state_of_condition();

                    await given_an_installed_event_monitored_item(test, subscription);

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    // we_should_verify_that_no_event_has_been_raised_yet
                    test.spy_monitored_item1_changes.callCount.should.eql(0);
                    alarmNode.confirmedState!.getValue().should.eql(true, "confirmedState supposed to be set");
                    alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");


                    // 1. Then the Alarm goes active.
                    await alarm_goes_active();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    //  we_should_verify_that_an_event_has_been_raised
                    test.spy_monitored_item1_changes.callCount.should.eql(1, "an event should have been raised");

                    dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                    //xx dump_field_values(fields,dataValues);

                    eventId_Step0 = extract_value_for_field("EventId", dataValues).value;
                    should(eventId_Step0).be.instanceOf(Buffer);
                    extract_value_for_field("BranchId", dataValues).value.should.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues).value.should.eql(levelNode.browseName.toString());

                    extract_value_for_field("ActiveState", dataValues).value.text.should.eql("Active");
                    extract_value_for_field("ActiveState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("AckedState", dataValues).value.text.should.eql("Unacknowledged");
                    extract_value_for_field("AckedState.Id", dataValues).value.should.eql(false);
                    extract_value_for_field("ConfirmedState", dataValues).value.text.should.eql("Confirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("Retain", dataValues).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    //
                    alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");


                    test.spy_monitored_item1_changes.resetHistory();

                    // 2. Condition acknowledged requires Confirm
                    await condition_acknowledged_requires_confirm();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    // we_should_verify_that_a_second_event_has_been_raised
                    // showing that alarm has been Acknowledged
                    // and need to be confirmed
                    test.spy_monitored_item1_changes.callCount.should.eql(2);

                    dataValues = test.spy_monitored_item1_changes.getCall(1).args[0];
                    //xx dump_field_values(fields,dataValues);
                    // ns=0;i=8944 AuditConditionAcknowledgeEventType
                    extract_value_for_field("EventType", dataValues).value.toString().should.eql("ns=0;i=8944");

                    dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                    //xx dump_field_values(fields,dataValues);

                    eventId_Step2 = extract_value_for_field("EventId", dataValues).value;
                    should(eventId_Step2).be.instanceOf(Buffer);

                    extract_value_for_field("EventType", dataValues).value.toString().should.eql(eventTypeNodeId);

                    eventId_Step2!
                        .toString("hex")
                        .should.not.eql(eventId_Step0!.toString("hex"), "eventId must have changed");

                    extract_value_for_field("BranchId", dataValues).value.should.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues).value.should.eql(levelNode.browseName.toString());

                    extract_value_for_field("ActiveState", dataValues).value.text.should.eql("Active");
                    extract_value_for_field("ActiveState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("AckedState", dataValues).value.text.should.eql("Acknowledged");
                    extract_value_for_field("AckedState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("ConfirmedState", dataValues).value.text.should.eql("Unconfirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues).value.should.eql(false);
                    extract_value_for_field("Retain", dataValues).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();

                    // 3.  Alarm goes inactive.
                    await alarm_goes_inactive(),

                        await wait_a_little_bit_to_let_events_to_be_processed();

                    //  we_should_verify_that_a_third_event_has_been_raised() 
                    // showing that alarm has been Acknowledged
                    // and need to be confirmed
                    test.spy_monitored_item1_changes.callCount.should.eql(1);

                    dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                    // dump_field_values(fields,dataValues);

                    eventId_Step0 = extract_value_for_field("EventId", dataValues).value;
                    should(eventId_Step0).be.instanceOf(Buffer);

                    // ns=0;i=9341 => ExclusiveLimitAlarmType
                    extract_value_for_field("EventType", dataValues).value.toString().should.eql(eventTypeNodeId);
                    extract_value_for_field("BranchId", dataValues).value.should.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues).value.should.eql(levelNode.browseName.toString());

                    extract_value_for_field("ActiveState", dataValues).value.text.should.eql("Inactive");
                    extract_value_for_field("ActiveState.Id", dataValues).value.should.eql(false);
                    extract_value_for_field("AckedState", dataValues).value.text.should.eql("Acknowledged");
                    extract_value_for_field("AckedState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("ConfirmedState", dataValues).value.text.should.eql("Unconfirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues).value.should.eql(false);
                    extract_value_for_field("Retain", dataValues).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    //
                    alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();

                    // 4. Confirmed
                    await condition_is_confirmed();

                    await wait_a_little_bit_to_let_events_to_be_processed();
                    await wait_a_little_bit_to_let_events_to_be_processed();


                    //  we_should_verify_that_a_third_event_has_been_raised
                    test.spy_monitored_item1_changes.callCount.should.eql(3);

                    //  i=2829 => AuditConditionCommentEventType
                    dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                    extract_value_for_field("EventType", dataValues).value.toString().should.eql("ns=0;i=2829");

                    //  i=8961 => AuditConditionConfirmEventType
                    dataValues = test.spy_monitored_item1_changes.getCall(1).args[0];
                    extract_value_for_field("EventType", dataValues).value.toString().should.eql("ns=0;i=8961");

                    //  i=9341 => ExclusiveLimitAlarmType
                    dataValues = test.spy_monitored_item1_changes.getCall(2).args[0];
                    extract_value_for_field("EventType", dataValues).value.toString().should.eql(eventTypeNodeId);
                    //xx dump_field_values(fields,dataValues);
                    extract_value_for_field("BranchId", dataValues).value.should.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues).value.should.eql(levelNode.browseName.toString());

                    extract_value_for_field("ActiveState", dataValues).value.text.should.eql("Inactive");
                    extract_value_for_field("ActiveState.Id", dataValues).value.should.eql(false);
                    extract_value_for_field("AckedState", dataValues).value.text.should.eql("Acknowledged");
                    extract_value_for_field("AckedState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("ConfirmedState", dataValues).value.text.should.eql("Confirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("Retain", dataValues).value.should.eql(false);
                    extract_node_id_value_for_condition_type_field(dataValues)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();


                    // 5. Alarm goes active.
                    await alarm_goes_active();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    //  we_should_verify_that_a_fourth_event_has_been_raised

                    test.spy_monitored_item1_changes.callCount.should.eql(1);
                    dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];

                    //  i=9341 => ExclusiveLimitAlarmType
                    extract_value_for_field("EventType", dataValues).value.toString().should.eql(eventTypeNodeId);
                    //xx dump_field_values(fields,dataValues);
                    extract_value_for_field("BranchId", dataValues).value.should.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues).value.should.eql(levelNode.browseName.toString());

                    extract_value_for_field("ActiveState", dataValues).value.text.should.eql("Active");
                    extract_value_for_field("ActiveState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("AckedState", dataValues).value.text.should.eql("Unacknowledged");
                    extract_value_for_field("AckedState.Id", dataValues).value.should.eql(false);
                    extract_value_for_field("ConfirmedState", dataValues).value.text.should.eql("Confirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues).value.should.eql(true);
                    extract_value_for_field("Retain", dataValues).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();

                    // 6. Alarm goes inactive.
                    await alarm_goes_inactive();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    //  we_should_verify_that_a_fifth_and_sixth_event_have_been_raised

                    test.spy_monitored_item1_changes.callCount.should.eql(2);

                    const dataValues7 = test.spy_monitored_item1_changes.getCall(0).args[0];
                    //xx dump_field_values(fields,dataValues7);

                    // event value for branch #1 -----------------------------------------------------
                    //  i=9341 => ExclusiveLimitAlarmType
                    extract_value_for_field("EventType", dataValues7).value.toString().should.eql(eventTypeNodeId);
                    extract_value_for_field("BranchId", dataValues7).value.should.not.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues7).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues7).value.should.eql(
                        levelNode.browseName.toString()
                    );
                    branch1_NodeId = extract_value_for_field("BranchId", dataValues7).value;
                    branch1_EventId = extract_value_for_field("EventId", dataValues7).value;

                    extract_value_for_field("ActiveState", dataValues7).value.text.should.eql("Active");
                    extract_value_for_field("ActiveState.Id", dataValues7).value.should.eql(true);
                    extract_value_for_field("AckedState", dataValues7).value.text.should.eql("Unacknowledged");
                    extract_value_for_field("AckedState.Id", dataValues7).value.should.eql(false);
                    extract_value_for_field("ConfirmedState", dataValues7).value.text.should.eql("Confirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues7).value.should.eql(true);
                    extract_value_for_field("Retain", dataValues7).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues7)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    const dataValues8 = test.spy_monitored_item1_changes.getCall(1).args[0];
                    //xx dump_field_values(fields, dataValues8);

                    //  i=9341 => ExclusiveLimitAlarmType
                    extract_value_for_field("EventType", dataValues8).value.toString().should.eql(eventTypeNodeId);
                    //xx dump_field_values(fields,dataValues);
                    extract_value_for_field("BranchId", dataValues8).value.should.eql(NodeId.nullNodeId);
                    //Xxx extract_value_for_field("ConditionName", dataValues8).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues8).value.should.eql(
                        levelNode.browseName.toString()
                    );

                    extract_value_for_field("ActiveState", dataValues8).value.text.should.eql("Inactive");
                    extract_value_for_field("ActiveState.Id", dataValues8).value.should.eql(false);
                    extract_value_for_field("AckedState", dataValues8).value.text.should.eql("Acknowledged");
                    extract_value_for_field("AckedState.Id", dataValues8).value.should.eql(true);
                    extract_value_for_field("ConfirmedState", dataValues8).value.text.should.eql("Confirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues8).value.should.eql(true);
                    extract_value_for_field("Retain", dataValues8).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues8)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    alarmNode.getBranchCount().should.eql(1, " Expecting one extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();


                    // 7. b) Prior state needs acknowledgment. Branch #1 created.
                    //       Timestamps for the Events #6 and #7 is identical.
                    await verify_that_branch_one_is_created();

                    // 8. Alarm goes active again.
                    await alarm_goes_active();

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    // c we_should_verify_that_a_new_event_is_raised
                    test.spy_monitored_item1_changes.callCount.should.eql(1);
                    const dataValues9 = test.spy_monitored_item1_changes.getCall(0).args[0];
                    // dump_field_values(fields,dataValues);
                    //  i=9341 => ExclusiveLimitAlarmType
                    extract_value_for_field("EventType", dataValues9).value.toString().should.eql(eventTypeNodeId);
                    //xx dump_field_values(fields,dataValues);
                    extract_value_for_field("BranchId", dataValues9).value.should.eql(NodeId.nullNodeId);
                    //xx extract_value_for_field("ConditionName", dataValues9).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValues9).value.should.eql(
                        levelNode.browseName.toString()
                    );

                    extract_value_for_field("ActiveState", dataValues9).value.text.should.eql("Active");
                    extract_value_for_field("ActiveState.Id", dataValues9).value.should.eql(true);
                    extract_value_for_field("AckedState", dataValues9).value.text.should.eql("Unacknowledged");
                    extract_value_for_field("AckedState.Id", dataValues9).value.should.eql(false);
                    extract_value_for_field("ConfirmedState", dataValues9).value.text.should.eql("Confirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValues9).value.should.eql(true);
                    extract_value_for_field("Retain", dataValues9).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValues9)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    alarmNode.getBranchCount().should.eql(1, " Expecting one extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();


                    // 9. Prior state acknowledged, Confirm required.
                    alarmNode.getBranchCount().should.eql(1, " Expecting one extra branch apart from current branch");

                    debugLog("9. Prior state acknowledged, Confirm required.");
                    const conditionId = alarmNode.nodeId;
                    const eventId = branch1_EventId!;
                    debugLog("EventId = ", eventId);

                    // console.log(" EventID ", eventId.toString("hex"));
                    // console.log(alarmNode.getBranches().map(function(branch){
                    //     return branch.getBranchId().toString() + " " + branch.getEventId().toString("hex")
                    // }).join(" "));

                    await session.acknowledgeCondition(conditionId, eventId, "Branch#1 Some comment");

                    await wait_a_little_bit_to_let_events_to_be_processed();

                    // we_should_verify_that_an_event_is_raised_for_branch_and_that_confirm_is_false
                    test.spy_monitored_item1_changes.callCount.should.eql(2);
                    const dataValuesA = test.spy_monitored_item1_changes.getCall(1).args[0];
                    // dump_field_values(fields, dataValuesA);

                    // ns=0;i=8944 AuditConditionAcknowledgeEventType
                    extract_value_for_field("EventType", dataValuesA).value.toString().should.eql("ns=0;i=8944");
                    // xx should(extract_value_for_field("BranchId",   dataValuesA).value).eql(branch1_NodeId);

                    const dataValuesB = test.spy_monitored_item1_changes.getCall(0).args[0];

                    extract_value_for_field("EventType", dataValuesB).value.toString().should.eql(eventTypeNodeId);
                    extract_value_for_field("BranchId", dataValuesB).value.should.eql(branch1_NodeId);
                    // update last known event of branch1_EventId
                    branch1_EventId = extract_value_for_field("EventId", dataValuesB).value;

                    extract_value_for_field("ActiveState", dataValuesB).value.text.should.eql("Active");
                    extract_value_for_field("ActiveState.Id", dataValuesB).value.should.eql(true);
                    //xx extract_value_for_field("ConditionName", dataValuesB).value.should.eql("Test2");
                    extract_value_for_field("SourceName", dataValuesB).value.should.eql(
                        levelNode.browseName.toString()
                    );
                    extract_value_for_field("AckedState", dataValuesB).value.text.should.eql("Acknowledged");
                    extract_value_for_field("AckedState.Id", dataValuesB).value.should.eql(true);
                    extract_value_for_field("ConfirmedState", dataValuesB).value.text.should.eql("Unconfirmed");
                    extract_value_for_field("ConfirmedState.Id", dataValuesB).value.should.eql(false);
                    extract_value_for_field("Retain", dataValuesB).value.should.eql(true);
                    extract_node_id_value_for_condition_type_field(dataValuesB)
                        .value.toString()
                        .should.eql(alarmNode.nodeId.toString());

                    alarmNode.getBranchCount().should.eql(1, " Expecting one extra branch apart from current branch");

                    test.spy_monitored_item1_changes.resetHistory();

                    // 10. b) Alarm goes inactive again.
                    await alarm_goes_inactive();

                    await wait_a_little_bit_to_let_events_to_be_processed();
                    // 11. Prior state needs acknowledgment. Branch #2 created.

                    // we_should_verify_that_a_second_branch_is_created
                    {
                        test.spy_monitored_item1_changes.callCount.should.eql(2);

                        // -----------------------------  Event on a second  Branch !
                        const dataValuesA = test.spy_monitored_item1_changes.getCall(0).args[0];
                        //  i=9341 => ExclusiveLimitAlarmType
                        extract_value_for_field("EventType", dataValuesA).value.toString().should.eql(eventTypeNodeId);
                        extract_value_for_field("BranchId", dataValuesA).value.should.not.eql(NodeId.nullNodeId);
                        extract_value_for_field("BranchId", dataValuesA).value.should.not.eql(branch1_NodeId);
                        branch2_NodeId = extract_value_for_field("BranchId", dataValuesA).value;
                        // update last known event of branch2_NodeId
                        branch2_EventId = extract_value_for_field("EventId", dataValuesA).value;

                        extract_value_for_field("ActiveState", dataValuesA).value.text.should.eql("Active");
                        extract_value_for_field("ActiveState.Id", dataValuesA).value.should.eql(true);

                        //xx extract_value_for_field("ConditionName", dataValuesA).value.should.eql("Test2");
                        extract_value_for_field("SourceName", dataValuesA).value.should.eql(
                            levelNode.browseName.toString()
                        );

                        extract_value_for_field("AckedState", dataValuesA).value.text.should.eql("Unacknowledged");
                        extract_value_for_field("AckedState.Id", dataValuesA).value.should.eql(false);
                        extract_value_for_field("ConfirmedState", dataValuesA).value.text.should.eql("Confirmed");
                        extract_value_for_field("ConfirmedState.Id", dataValuesA).value.should.eql(true);
                        extract_value_for_field("Retain", dataValuesA).value.should.eql(true);
                        extract_node_id_value_for_condition_type_field(dataValuesA)
                            .value.toString()
                            .should.eql(alarmNode.nodeId.toString());

                        // -----------------------------  Event on main branch !
                        const dataValuesB = test.spy_monitored_item1_changes.getCall(1).args[0];
                        //  i=9341 => ExclusiveLimitAlarmType
                        extract_value_for_field("EventType", dataValuesB).value.toString().should.eql(eventTypeNodeId);
                        extract_value_for_field("BranchId", dataValuesB).value.should.eql(NodeId.nullNodeId);
                        extract_value_for_field("ActiveState", dataValuesB).value.text.should.eql("Inactive");
                        extract_value_for_field("ActiveState.Id", dataValuesB).value.should.eql(false);
                        extract_value_for_field("AckedState", dataValuesB).value.text.should.eql("Acknowledged");
                        extract_value_for_field("AckedState.Id", dataValuesB).value.should.eql(true);
                        extract_value_for_field("ConfirmedState", dataValuesB).value.text.should.eql("Confirmed");
                        extract_value_for_field("ConfirmedState.Id", dataValuesB).value.should.eql(true);
                        extract_value_for_field("Retain", dataValuesB).value.should.eql(true);
                        extract_node_id_value_for_condition_type_field(dataValuesB)
                            .value.toString()
                            .should.eql(alarmNode.nodeId.toString());

                        alarmNode.getBranchCount().should.eql(2, " Expecting two extra branches apart from current branch");
                        test.spy_monitored_item1_changes.resetHistory();
                    }

                    // 12. Prior state confirmed. Branch #1 deleted.

                    // branch_one_is_confirmed_verify_branch_one_is_deleted
                    {
                        debugLog("Confirming branchId with eventId  = ", branch1_EventId!.toString("hex"));
                        await session.confirmCondition(alarmNode.nodeId, branch1_EventId!, "Some Message");

                        await wait_a_little_bit_to_let_events_to_be_processed();

                        // function we_should_verify_that_branch_one_is_deleted(callback) {
                        {

                        } alarmNode.getBranchCount().should.eql(1, " Expecting one extra branch apart from current branch");

                        test.spy_monitored_item1_changes.callCount.should.eql(3);

                        //  i=2829 => AuditConditionCommentEventType
                        dataValues = test.spy_monitored_item1_changes.getCall(0).args[0];
                        extract_value_for_field("EventType", dataValues).value.toString().should.eql("ns=0;i=2829");

                        // ns=0;i=8961 AuditConditionConfirmEventType
                        const dataValuesA = test.spy_monitored_item1_changes.getCall(1).args[0];
                        extract_value_for_field("EventType", dataValuesA).value.toString().should.eql("ns=0;i=8961");

                        const dataValuesB = test.spy_monitored_item1_changes.getCall(2).args[0];
                        //  i=9341 => ExclusiveLimitAlarmType
                        extract_value_for_field("EventType", dataValuesB).value.toString().should.eql(eventTypeNodeId);
                        extract_value_for_field("BranchId", dataValuesB).value.should.eql(branch1_NodeId);
                        extract_value_for_field("ActiveState", dataValuesB).value.text.should.eql("Active");
                        extract_value_for_field("ActiveState.Id", dataValuesB).value.should.eql(true);
                        extract_value_for_field("AckedState", dataValuesB).value.text.should.eql("Acknowledged");
                        extract_value_for_field("AckedState.Id", dataValuesB).value.should.eql(true);
                        extract_value_for_field("ConfirmedState", dataValuesB).value.text.should.eql("Confirmed");
                        extract_value_for_field("ConfirmedState.Id", dataValuesB).value.should.eql(true);
                        extract_value_for_field("Retain", dataValuesB).value.should.eql(false);

                        test.spy_monitored_item1_changes.resetHistory();
                    }
                    // 13. Prior state acknowledged, Auto Confirmed by system Branch #2 deleted.
                    await branch_two_acknowledged_and_auto_confirmed_by_system_verify_branch_two_is_deleted();

                    // 14. No longer of interest.
                    await wait_a_little_bit_to_let_events_to_be_processed();

                    //  we_should_verify_than_branch_one_is_no_longer_here
                    {
                        alarmNode.getBranchCount().should.eql(0, " Expecting no extra branch apart from current branch");

                        test.spy_monitored_item1_changes.callCount.should.eql(5);

                        const dataValues0 = test.spy_monitored_item1_changes.getCall(0).args[0];
                        const dataValues1 = test.spy_monitored_item1_changes.getCall(1).args[0];
                        const dataValues2 = test.spy_monitored_item1_changes.getCall(2).args[0];

                        const dataValuesA = test.spy_monitored_item1_changes.getCall(3).args[0];

                        // ns=0;i=8961 AuditConditionConfirmEventType
                        extract_value_for_field("EventType", dataValuesA).value.toString().should.eql("ns=0;i=8961");

                        const dataValuesB = test.spy_monitored_item1_changes.getCall(4).args[0];
                        //  i=9341 => ExclusiveLimitAlarmType
                        extract_value_for_field("EventType", dataValuesB).value.toString().should.eql(eventTypeNodeId);
                        extract_value_for_field("BranchId", dataValuesB).value.should.eql(branch2_NodeId);

                        extract_value_for_field("ActiveState", dataValuesB).value.text.should.eql("Active");
                        extract_value_for_field("ActiveState.Id", dataValuesB).value.should.eql(true);

                        extract_value_for_field("AckedState", dataValuesB).value.text.should.eql("Acknowledged");
                        extract_value_for_field("AckedState.Id", dataValuesB).value.should.eql(true);

                        extract_value_for_field("ConfirmedState", dataValuesB).value.text.should.eql("Confirmed");
                        extract_value_for_field("ConfirmedState.Id", dataValuesB).value.should.eql(true);
                        extract_value_for_field("Retain", dataValuesB).value.should.eql(false);

                    }
                });
        }

        it("A&C-10 Example of a Condition that maintains previous states via branches - with exclusive condition", async () => {
            // ns=0;i=9341 => ExclusiveLimitAlarmType
            await perform_test_with_condition("ns=0;i=9341", test.tankLevel, test.tankLevelCondition);
        });

        it("A&C-11 Example of a Condition that maintains previous states via branches - with non exclusive condition", async () => {
            // ns=0;i=9906 => NonExclusiveLimitAlarmType
            await perform_test_with_condition("ns=0;i=9906", test.tankLevel2, test.tankLevelCondition2);
        });
    });
};
